/*
 * AIEngine a new generation network intrusion detection system.
 *
 * Copyright (C) 2013-2023  Luis Campo Giralte
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public
 * License along with this library; if not, write to the
 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
 * Boston, MA  02110-1301, USA.
 *
 * Written by Luis Campo Giralte <luis.camp0.2009@gmail.com> 
 *
 */
#ifndef SRC_PROTOCOLS_SIP_SIPPROTOCOL_H_
#define SRC_PROTOCOLS_SIP_SIPPROTOCOL_H_

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#include "Protocol.h"
#include "StringCache.h"
#include <netinet/ip.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <iostream>
#include "Cache.h"
#include <unordered_map>
#include "regex/Regex.h"
#include "flow/FlowManager.h"
#include "SIPInfo.h"

namespace aiengine {

enum sip_state_code {
	SIP_NONE = 0x00,
        SIP_TRYING_CALL = 0x01,
        SIP_CALL_ESTABLISHED,
        SIP_FINISH_CALL,
        SIP_CALL_DONE 
};

class SIPProtocol: public Protocol {
public:
    	explicit SIPProtocol();
    	virtual ~SIPProtocol() {}

	static const uint16_t header_size = 0; // TODO

	uint16_t getId() const override { return 0x0000; }
	uint16_t getHeaderSize() const override { return header_size; }

        // Condition for say that a payload is SIP
	bool check(const Packet &packet) override;
	bool processPacket(Packet &packet) override { /* Nothing to process at packet level*/ return true; }
	void processFlow(Flow *flow) override;

	void statistics(std::basic_ostream<char> &out, int level, int32_t limit) const override;
	void statistics(Json &out, int level) const override;
	void statistics(Json &out, const std::string &map_name, int32_t limit) const override;

	void releaseCache() override; // Three caches will be clean 

        void setHeader(const uint8_t *raw_packet) override {
        
                header_ = raw_packet;
        }

	const uint8_t *getPayload() const { return header_; }

        void increaseAllocatedMemory(int value) override; 
        void decreaseAllocatedMemory(int value) override;
        
	void setFlowManager(FlowManagerPtrWeak flow_mng) { flow_mng_ = flow_mng; }

	uint64_t getCurrentUseMemory() const override;
	uint64_t getAllocatedMemory() const override;
	uint64_t getTotalAllocatedMemory() const override;

        void setDynamicAllocatedMemory(bool value) override; 
        bool isDynamicAllocatedMemory() const override; 

	uint32_t getTotalCacheMisses() const override;
	uint32_t getTotalEvents() const override { return total_events_; }

	CounterMap getCounters() const override; 
	void resetCounters() override;

	void releaseFlowInfo(Flow *flow) override;

#if defined(PYTHON_BINDING)
        boost::python::dict getCacheData(const std::string &name) const override;
        SharedPointer<Cache<StringCache>> getCache(const std::string &name) override;
#elif defined(RUBY_BINDING)
        VALUE getCacheData(const std::string &name) const;
#endif

#if defined(STAND_ALONE_TEST) || defined(TESTING)
	uint32_t getTotalRegisters() const { return total_registers_; }
	uint32_t getTotalInvitess() const { return total_invites_; }
	uint32_t getTotalPublishs() const { return total_publishs_; }
	uint32_t getTotalPings() const { return total_pings_; }
	uint32_t getTotalNotifies() const { return total_notifies_; }
	uint32_t getTotalOptions() const { return total_options_; }
	uint32_t getTotalInfos() const { return total_infos_; }
	uint32_t getTotalRefers() const { return total_refers_; }
	uint32_t getTotalCancels() const { return total_cancels_; }
	uint32_t getTotalMessages() const { return total_messages_; }
	uint32_t getTotalSubscribes() const { return total_subscribes_; }
	uint32_t getTotalAcks() const { return total_acks_; }
	uint32_t getTotalByes() const { return total_byes_; }
#endif

private:
	void attach_uri_to_flow(SIPInfo *info, const boost::string_ref &uri);
	void attach_from_to_flow(SIPInfo *info, const boost::string_ref &from);
	void attach_to_to_flow(SIPInfo *info, const boost::string_ref &to);
	void attach_via_to_flow(SIPInfo *info, const boost::string_ref &via);
	void extract_uri_value(SIPInfo *info, const boost::string_ref &header);
	void extract_from_value(SIPInfo *info, const boost::string_ref &header);
	void extract_to_value(SIPInfo *info, const boost::string_ref &header);
	void extract_via_value(SIPInfo *info, const boost::string_ref &header);
	void handle_invite(SIPInfo *info, const boost::string_ref &header);
	void handle_ok(SIPInfo *info, const boost::string_ref &header);
	void handle_bye(SIPInfo *info, const boost::string_ref &header);
	std::tuple<uint32_t, uint16_t> extract_ip_and_port_from_sdp(const boost::string_ref &hdr); 
	std::tuple<bool, int> get_sip_request_method(const boost::string_ref &hdr); 
	uint64_t compute_memory_used_by_maps() const;

	Regex sip_from_ {"From expression", "From: .*?\r\n"};
	Regex sip_to_ {"To expression", "To: .*?\r\n"};
	Regex sip_via_ {"Via expression", "Via: .*?\r\n"};
	const uint8_t *header_ = nullptr;
	uint32_t total_events_ = 0;

	// Some statistics of the SIP methods 
	uint32_t total_requests_ = 0;
	uint32_t total_responses_ = 0;
	uint32_t total_registers_ = 0;
	uint32_t total_invites_ = 0;
	uint32_t total_publishs_ = 0;
	uint32_t total_byes_ = 0;
	uint32_t total_acks_ = 0;
	uint32_t total_subscribes_ = 0;
	uint32_t total_messages_ = 0;
	uint32_t total_cancels_ = 0;
	uint32_t total_refers_ = 0;
	uint32_t total_infos_ = 0;
	uint32_t total_options_ = 0;
	uint32_t total_notifies_ = 0;
	uint32_t total_pings_ = 0;
	uint32_t total_sip_others_ = 0;

	Cache<SIPInfo>::CachePtr info_cache_ = Cache<SIPInfo>::CachePtr(new Cache<SIPInfo>("SIP Info cache"));
	Cache<StringCache>::CachePtr uri_cache_ = Cache<StringCache>::CachePtr(new Cache<StringCache>("Uri cache"));
	Cache<StringCache>::CachePtr via_cache_ = Cache<StringCache>::CachePtr(new Cache<StringCache>("Via cache"));
	Cache<StringCache>::CachePtr from_cache_ = Cache<StringCache>::CachePtr(new Cache<StringCache>("From cache"));
	Cache<StringCache>::CachePtr to_cache_ = Cache<StringCache>::CachePtr(new Cache<StringCache>("To cache"));

	StringMap uri_map_ {"SIP Uris", "Uri"};	
	StringMap via_map_ {"SIP Vias", "Via"};
	StringMap from_map_ {"SIP Froms", "From"};	
	StringMap to_map_ {"SIP Tos", "To"};	

	FlowManagerPtrWeak flow_mng_ = FlowManagerPtrWeak();
	Flow *current_flow_ = nullptr;
};

typedef std::shared_ptr<SIPProtocol> SIPProtocolPtr;

} // namespace aiengine

#endif  // SRC_PROTOCOLS_SIP_SIPPROTOCOL_H_
